/******************************************************************************
 * Copyright (C) 2015  671643387@qq.com
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along
 * with this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
 *
 *****************************************************************************/

#include "Ucop/Net/TcpSocket.h"
#include <cassert>
#include <event2/buffer.h>
#include "Ucop/System/Error.h"
#include "Ucop/Net/TcpConnection.h"
#include "Ucop/Net/TcpServer.h"


namespace Ucop {
namespace Net {

    TcpSocket::TcpSocket(void)
        : _Myid(-1)
        , _Mysock(-1)
        , _Mybev(NULL)
        , _Myhandler(NULL)
        , _Myhost("")
        , _Myport(0)
        , _MyerrorString("")
        , _MyerrorCode(0)
        , _MyfatalError(false)
        , _Myowner(NULL)
        , _Myrunnable(NULL)
    {

    }

    TcpSocket::~TcpSocket(void)
    {
        _Unintialize();
    }

    void TcpSocket::Close(void)
    {
        UCOP_ASSERT(_Myowner != NULL);
        _Myowner->_CloseSocket(this);
    }

    void TcpSocket::Send(const std::uint8_t *_Data, std::size_t _Size)
    {
        if (!_MyfatalError)
        {
            bufferevent_enable(_Mybev, EV_WRITE);
            if (bufferevent_write(_Mybev, _Data, _Size) != 0) {
                _OnError();
            }
        }
    }

    std::size_t TcpSocket::GetReceivedSize(void)
    {
        return evbuffer_get_length(bufferevent_get_input(_Mybev));
    }

    int TcpSocket::Read(std::uint8_t *_Data, std::size_t _Size)
    {
        return evbuffer_remove(bufferevent_get_input(_Mybev), _Data, _Size);
    }

    int TcpSocket::ReadPeek(std::uint8_t *_Data, std::size_t _Size)
    {
        return evbuffer_copyout(bufferevent_get_input(_Mybev), _Data, _Size);
    }

    std::size_t TcpSocket::ReadSkip(std::size_t _Size)
    {
        if (evbuffer_drain(bufferevent_get_input(_Mybev), _Size) == 0) {
            return _Size;
        }
        return 0;
    }

    void TcpSocket::EnableReceive(bool _Enable)
    {
        if (_Enable) {
            bufferevent_enable(_Mybev, EV_READ);
        }
        else {
            bufferevent_disable(_Mybev, EV_READ);
        }
    }

    void TcpSocket::EnableSend(bool _Enable)
    {
        if (_Enable) {
            bufferevent_enable(_Mybev, EV_WRITE);
        }
        else {
            bufferevent_disable(_Mybev, EV_WRITE);
        }
    }

    void TcpSocket::_Unintialize(void)
    {
        if (_Mybev)
        {
            bufferevent_disable(_Mybev, EV_READ);
            bufferevent_free(_Mybev);
            _Mybev = NULL;
            _Mysock = -1;
        }
    }

    void TcpSocket::_OnError(void)
    {
        if (_MyfatalError) {
            return;
        }
        _MyfatalError = true;

        EnableSend(false);
    }

    void TcpSocket::_ReadCallback(struct bufferevent *, void *_Userptr)
    {
        TcpSocket *_Sock = static_cast<TcpSocket*>(_Userptr);
        UCOP_ASSERT(_Sock);
        _Sock->_Myhandler->OnRead();
    }

    void TcpSocket::_WriteCallback(struct bufferevent *, void *_Userptr)
    {
        TcpSocket *_Sock = static_cast<TcpSocket*>(_Userptr);
        UCOP_ASSERT(_Sock);

        if (_Sock->_MyfatalError)
        {
            _Sock->EnableSend(false);
            _Sock->_Myhandler->OnWrite();
        }
    }

    void TcpSocket::_EventCallback(struct bufferevent *, short _Events, void *_Userptr)
    {
        TcpSocket *_Sock = static_cast<TcpSocket*>(_Userptr);
        UCOP_ASSERT(_Sock);

        if (_Events & BEV_EVENT_EOF) {
            _Sock->_OnError();
            _Sock->_Myhandler->OnEof();
        }
        else if (_Events & BEV_EVENT_ERROR) {
            _Sock->_MyerrorCode = System::GetLastNetError();
            System::GetErrorString(_Sock->_MyerrorString, _Sock->_MyerrorCode);
            _Sock->_OnError();
            _Sock->_Myhandler->OnError();
        }
        else if (_Events & BEV_EVENT_TIMEOUT) {
            int _Flags = 0;
            if (_Events & BEV_EVENT_READING) {
                _Flags |= EF_READ;
            }
            if (_Events & BEV_EVENT_WRITING) {
                _Flags |= EF_WRITE;
            }
            _Sock->_Myhandler->OnTimeout(_Flags);
        }
    }

}}